home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Cache / Lite.php < prev    next >
PHP Script  |  2004-10-01  |  18KB  |  616 lines

  1. <?php
  2.  
  3. /**
  4. * Fast, light and safe Cache Class
  5. *
  6. * Cache_Lite is a fast, light and safe cache system. It's optimized
  7. * for file containers. It is fast and safe (because it uses file
  8. * locking and/or anti-corruption tests).
  9. *
  10. * There are some examples in the 'docs/examples' file
  11. * Technical choices are described in the 'docs/technical' file
  12. *
  13. * A tutorial is available in english at this url :
  14. * http://www.pearfr.org/index.php/en/article/cache_lite
  15. * (big thanks to Pierre-Alain Joye for the translation)
  16. *
  17. * The same tutorial is also available in french at this url :
  18. * http://www.pearfr.org/index.php/fr/article/cache_lite
  19. *
  20. * Memory Caching is from an original idea of
  21. * Mike BENOIT <ipso@snappymail.ca>
  22. *
  23. * @package Cache_Lite
  24. * @category Caching
  25. * @version $Id: Lite.php,v 1.20 2004/07/14 10:24:03 fab Exp $
  26. * @author Fabien MARTY <fab@php.net>
  27. */
  28.  
  29. define('CACHE_LITE_ERROR_RETURN', 1);
  30. define('CACHE_LITE_ERROR_DIE', 8);
  31.  
  32. class Cache_Lite
  33. {
  34.  
  35.     // --- Private properties ---
  36.  
  37.     /**
  38.     * Directory where to put the cache files
  39.     * (make sure to add a trailing slash)
  40.     *
  41.     * @var string $_cacheDir
  42.     */
  43.     var $_cacheDir = '/tmp/';
  44.  
  45.     /**
  46.     * Enable / disable caching
  47.     *
  48.     * (can be very usefull for the debug of cached scripts)
  49.     *
  50.     * @var boolean $_caching
  51.     */
  52.     var $_caching = true;
  53.  
  54.     /**
  55.     * Cache lifetime (in seconds)
  56.     *
  57.     * @var int $_lifeTime
  58.     */
  59.     var $_lifeTime = 3600;
  60.  
  61.     /**
  62.     * Enable / disable fileLocking
  63.     *
  64.     * (can avoid cache corruption under bad circumstances)
  65.     *
  66.     * @var boolean $_fileLocking
  67.     */
  68.     var $_fileLocking = true;
  69.  
  70.     /**
  71.     * Timestamp of the last valid cache
  72.     *
  73.     * @var int $_refreshTime
  74.     */
  75.     var $_refreshTime;
  76.  
  77.     /**
  78.     * File name (with path)
  79.     *
  80.     * @var string $_file
  81.     */
  82.     var $_file;
  83.  
  84.     /**
  85.     * Enable / disable write control (the cache is read just after writing to detect corrupt entries)
  86.     *
  87.     * Enable write control will lightly slow the cache writing but not the cache reading
  88.     * Write control can detect some corrupt cache files but maybe it's not a perfect control
  89.     *
  90.     * @var boolean $_writeControl
  91.     */
  92.     var $_writeControl = true;
  93.  
  94.     /**
  95.     * Enable / disable read control
  96.     *
  97.     * If enabled, a control key is embeded in cache file and this key is compared with the one
  98.     * calculated after the reading.
  99.     *
  100.     * @var boolean $_writeControl
  101.     */
  102.     var $_readControl = true;
  103.  
  104.     /**
  105.     * Type of read control (only if read control is enabled)
  106.     *
  107.     * Available values are :
  108.     * 'md5' for a md5 hash control (best but slowest)
  109.     * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
  110.     * 'strlen' for a length only test (fastest)
  111.     *
  112.     * @var boolean $_readControlType
  113.     */
  114.     var $_readControlType = 'crc32';
  115.  
  116.     /**
  117.     * Pear error mode (when raiseError is called)
  118.     *
  119.     * (see PEAR doc)
  120.     *
  121.     * @see setToDebug()
  122.     * @var int $_pearErrorMode
  123.     */
  124.     var $_pearErrorMode = CACHE_LITE_ERROR_RETURN;
  125.     
  126.     /**
  127.     * Current cache id
  128.     *
  129.     * @var string $_id
  130.     */
  131.     var $_id;
  132.  
  133.     /**
  134.     * Current cache group
  135.     *
  136.     * @var string $_group
  137.     */
  138.     var $_group;
  139.  
  140.     /**
  141.     * Enable / Disable "Memory Caching"
  142.     *
  143.     * NB : There is no lifetime for memory caching ! 
  144.     *
  145.     * @var boolean $_memoryCaching
  146.     */
  147.     var $_memoryCaching = false;
  148.  
  149.     /**
  150.     * Enable / Disable "Only Memory Caching"
  151.     * (be carefull, memory caching is "beta quality")
  152.     *
  153.     * @var boolean $_onlyMemoryCaching
  154.     */
  155.     var $_onlyMemoryCaching = false;
  156.  
  157.     /**
  158.     * Memory caching array
  159.     *
  160.     * @var array $_memoryCachingArray
  161.     */
  162.     var $_memoryCachingArray = array();
  163.  
  164.     /**
  165.     * Memory caching counter
  166.     *
  167.     * @var int $memoryCachingCounter
  168.     */
  169.     var $_memoryCachingCounter = 0;
  170.  
  171.     /**
  172.     * Memory caching limit
  173.     *
  174.     * @var int $memoryCachingLimit
  175.     */
  176.     var $_memoryCachingLimit = 1000;
  177.     
  178.     /**
  179.     * File Name protection
  180.     *
  181.     * if set to true, you can use any cache id or group name
  182.     * if set to false, it can be faster but cache ids and group names
  183.     * will be used directly in cache file names so be carefull with
  184.     * special characters...
  185.     *
  186.     * @var boolean $fileNameProtection
  187.     */
  188.     var $_fileNameProtection = true;
  189.     
  190.     /**
  191.     * Enable / disable automatic serialization
  192.     *
  193.     * it can be used to save directly datas which aren't strings
  194.     * (but it's slower)    
  195.     *
  196.     * @var boolean $_serialize
  197.     */
  198.     var $_automaticSerialization = false;
  199.     
  200.     // --- Public methods ---
  201.  
  202.     /**
  203.     * Constructor
  204.     *
  205.     * $options is an assoc. Available options are :
  206.     * $options = array(
  207.     *     'cacheDir' => directory where to put the cache files (string),
  208.     *     'caching' => enable / disable caching (boolean),
  209.     *     'lifeTime' => cache lifetime in seconds (int),
  210.     *     'fileLocking' => enable / disable fileLocking (boolean),
  211.     *     'writeControl' => enable / disable write control (boolean),
  212.     *     'readControl' => enable / disable read control (boolean),
  213.     *     'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string),
  214.     *     'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int),
  215.     *     'memoryCaching' => enable / disable memory caching (boolean),
  216.     *     'onlyMemoryCaching' => enable / disable only memory caching (boolean),
  217.     *     'memoryCachingLimit' => max nbr of records to store into memory caching (int),
  218.     *     'fileNameProtection' => enable / disable automatic file name protection (boolean),
  219.     *     'automaticSerialization' => enable / disable automatic serialization (boolean)
  220.     * );
  221.     *
  222.     * @param array $options options
  223.     * @access public
  224.     */
  225.     function Cache_Lite($options = array(NULL))
  226.     {
  227.         $availableOptions = array('automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode');
  228.         foreach($options as $key => $value) {
  229.             if(in_array($key, $availableOptions)) {
  230.                 $property = '_'.$key;
  231.                 $this->$property = $value;
  232.             }
  233.         }
  234.         $this->_refreshTime = time() - $this->_lifeTime;
  235.     }
  236.     
  237.     /**
  238.     * Test if a cache is available and (if yes) return it
  239.     *
  240.     * @param string $id cache id
  241.     * @param string $group name of the cache group
  242.     * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
  243.     * @return string data of the cache (or false if no cache available)
  244.     * @access public
  245.     */
  246.     function get($id, $group = 'default', $doNotTestCacheValidity = false)
  247.     {
  248.         $this->_id = $id;
  249.         $this->_group = $group;
  250.         $data = false;
  251.         if ($this->_caching) {
  252.             $this->_setFileName($id, $group);
  253.             if ($this->_memoryCaching) {
  254.                 if (isset($this->_memoryCachingArray[$this->_file])) {
  255.                     if ($this->_automaticSerialization) {
  256.                         return unserialize($this->_memoryCachingArray[$this->_file]);
  257.                     } else {
  258.                         return $this->_memoryCachingArray[$this->_file];
  259.                     }
  260.                 } else {
  261.                     if ($this->_onlyMemoryCaching) {
  262.                         return false;
  263.                     }
  264.                 }
  265.             }
  266.             if ($doNotTestCacheValidity) {
  267.                 if (file_exists($this->_file)) {
  268.                     $data = $this->_read();
  269.                 }
  270.             } else {
  271.                 if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) {
  272.                     $data = $this->_read();
  273.                 }
  274.             }
  275.             if (($data) and ($this->_memoryCaching)) {
  276.                 $this->_memoryCacheAdd($this->_file, $data);
  277.             }
  278.             if (($this->_automaticSerialization) and (is_string($data))) {
  279.                 $data = unserialize($data);
  280.             }
  281.             return $data;
  282.         }
  283.         return false;
  284.     }
  285.     
  286.     /**
  287.     * Save some data in a cache file
  288.     *
  289.     * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on)
  290.     * @param string $id cache id
  291.     * @param string $group name of the cache group
  292.     * @return boolean true if no problem
  293.     * @access public
  294.     */
  295.     function save($data, $id = NULL, $group = 'default')
  296.     {
  297.         if ($this->_caching) {
  298.             if ($this->_automaticSerialization) {
  299.                 $data = serialize($data);
  300.             }
  301.             if (isset($id)) {
  302.                 $this->_setFileName($id, $group);
  303.             }
  304.             if ($this->_memoryCaching) {
  305.                 $this->_memoryCacheAdd($this->_file, $data);
  306.                 if ($this->_onlyMemoryCaching) {
  307.                     return true;
  308.                 }
  309.             }
  310.             if ($this->_writeControl) {
  311.                 if (!$this->_writeAndControl($data)) {
  312.                     @touch($this->_file, time() - 2*abs($this->_lifeTime));
  313.                     return false;
  314.                 } else {
  315.                     return true;
  316.                 }
  317.             } else {
  318.                 return $this->_write($data);
  319.             }
  320.         }
  321.         return false;
  322.     }
  323.  
  324.     /**
  325.     * Remove a cache file
  326.     *
  327.     * @param string $id cache id
  328.     * @param string $group name of the cache group
  329.     * @return boolean true if no problem
  330.     * @access public
  331.     */
  332.     function remove($id, $group = 'default')
  333.     {
  334.         $this->_setFileName($id, $group);
  335.         if ($this->_memoryCaching) {
  336.             if (isset($this->_memoryCachingArray[$this->_file])) {
  337.                 unset($this->_memoryCachingArray[$this->_file]);
  338.                 $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
  339.             }
  340.             if ($this->_onlyMemoryCaching) {
  341.                 return true;
  342.             }
  343.         }
  344.         if (!@unlink($this->_file)) {
  345.             $this->raiseError('Cache_Lite : Unable to remove cache !', -3);   
  346.             return false;
  347.         }
  348.         return true;
  349.     }
  350.  
  351.     /**
  352.     * Clean the cache
  353.     *
  354.     * if no group is specified all cache files will be destroyed
  355.     * else only cache files of the specified group will be destroyed
  356.     *
  357.     * @param string $group name of the cache group
  358.     * @return boolean true if no problem
  359.     * @access public
  360.     */
  361.     function clean($group = false)     
  362.     {
  363.         if ($this->_fileNameProtection) {
  364.             $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
  365.         } else {
  366.             $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
  367.         }
  368.         if ($this->_memoryCaching) {
  369.             while (list($key, $value) = each($this->_memoryCachingArray)) {
  370.                 if (strpos($key, $motif, 0)) {
  371.                     unset($this->_memoryCachingArray[$key]);
  372.                     $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
  373.                 }
  374.             }
  375.             if ($this->_onlyMemoryCaching) {
  376.                 return true;
  377.             }
  378.         }
  379.         if (!($dh = opendir($this->_cacheDir))) {
  380.             $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
  381.             return false;
  382.         }
  383.         while ($file = readdir($dh)) {
  384.             if (($file != '.') && ($file != '..')) {
  385.                 $file = $this->_cacheDir . $file;
  386.                 if (is_file($file)) {
  387.                     if (strpos($file, $motif, 0)) {
  388.                         if (!@unlink($file)) {
  389.                             $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
  390.                             return false;
  391.                         }
  392.                     }
  393.                 }
  394.             }
  395.         }
  396.         return true;
  397.     }
  398.     
  399.     /**
  400.     * Set to debug mode
  401.     *
  402.     * When an error is found, the script will stop and the message will be displayed
  403.     * (in debug mode only).
  404.     *
  405.     * @access public
  406.     */
  407.     function setToDebug()
  408.     {
  409.         $this->_pearErrorMode = CACHE_LITE_ERROR_DIE;
  410.     }
  411.  
  412.     /**
  413.     * Set a new life time
  414.     *
  415.     * @param int $newLifeTime new life time (in seconds)
  416.     * @access public
  417.     */
  418.     function setLifeTime($newLifeTime)
  419.     {
  420.         $this->_lifeTime = $newLifeTime;
  421.         $this->_refreshTime = time() - $newLifeTime;
  422.     }
  423.  
  424.     /**
  425.     *
  426.     * @access public
  427.     */
  428.     function saveMemoryCachingState($id, $group = 'default')
  429.     {
  430.         if ($this->_caching) {
  431.             $array = array(
  432.                 'counter' => $this->_memoryCachingCounter,
  433.                 'array' => $this->_memoryCachingState
  434.             );
  435.             $data = serialize($array);
  436.             $this->save($data, $id, $group);
  437.         }
  438.     }
  439.  
  440.     /**
  441.     *
  442.     * @access public
  443.     */
  444.     function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false)
  445.     {
  446.         if ($this->_caching) {
  447.             if ($data = $this->get($id, $group, $doNotTestCacheValidity)) {
  448.                 $array = unserialize($data);
  449.                 $this->_memoryCachingCounter = $array['counter'];
  450.                 $this->_memoryCachingArray = $array['array'];
  451.             }
  452.         }
  453.     }
  454.     
  455.     /**
  456.     * Return the cache last modification time
  457.     *
  458.     * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
  459.     *
  460.     * @return int last modification time
  461.     */
  462.     function lastModified() {
  463.         return filemtime($this->_file);
  464.     }
  465.     
  466.     /**
  467.     * Trigger a PEAR error
  468.     *
  469.     * To improve performances, the PEAR.php file is included dynamically.
  470.     * The file is so included only when an error is triggered. So, in most
  471.     * cases, the file isn't included and perfs are much better.
  472.     *
  473.     * @param string $msg error message
  474.     * @param int $code error code
  475.     * @access public
  476.     */
  477.     function raiseError($msg, $code)
  478.     {
  479.         include_once('PEAR.php');
  480.         PEAR::raiseError($msg, $code, $this->_pearErrorMode);
  481.     }
  482.  
  483.     // --- Private methods ---
  484.  
  485.     /**
  486.     *
  487.     * @access private
  488.     */
  489.     function _memoryCacheAdd($id, $data)
  490.     {
  491.         $this->_memoryCachingArray[$this->_file] = $data;
  492.         if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
  493.             list($key, $value) = each($this->_memoryCachingArray);
  494.             unset($this->_memoryCachingArray[$key]);
  495.         } else {
  496.             $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1;
  497.         }
  498.     }
  499.  
  500.     /**
  501.     * Make a file name (with path)
  502.     *
  503.     * @param string $id cache id
  504.     * @param string $group name of the group
  505.     * @access private
  506.     */
  507.     function _setFileName($id, $group)
  508.     {
  509.         if ($this->_fileNameProtection) {
  510.             $this->_file = ($this->_cacheDir.'cache_'.md5($group).'_'.md5($id));
  511.         } else {
  512.             $this->_file = $this->_cacheDir.'cache_'.$group.'_'.$id;
  513.         }
  514.     }
  515.     
  516.     /**
  517.     * Read the cache file and return the content
  518.     *
  519.     * @return string content of the cache file
  520.     * @access private
  521.     */
  522.     function _read()
  523.     {
  524.         $fp = @fopen($this->_file, "rb");
  525.         if ($this->_fileLocking) @flock($fp, LOCK_SH);
  526.         if ($fp) {
  527.             clearstatcache(); // because the filesize can be cached by PHP itself...
  528.             $length = @filesize($this->_file);
  529.             $mqr = get_magic_quotes_runtime();
  530.             set_magic_quotes_runtime(0);
  531.             if ($this->_readControl) {
  532.                 $hashControl = @fread($fp, 32);
  533.                 $length = $length - 32;
  534.             } 
  535.             $data = @fread($fp, $length);
  536.             set_magic_quotes_runtime($mqr);
  537.             if ($this->_fileLocking) @flock($fp, LOCK_UN);
  538.             @fclose($fp);
  539.             if ($this->_readControl) {
  540.                 $hashData = $this->_hash($data, $this->_readControlType);
  541.                 if ($hashData != $hashControl) {
  542.                     @touch($this->_file, time() - 2*abs($this->_lifeTime)); 
  543.                     return false;
  544.                 }
  545.             }
  546.             return $data;
  547.         }
  548.         $this->raiseError('Cache_Lite : Unable to read cache !', -2);   
  549.         return false;
  550.     }
  551.     
  552.     /**
  553.     * Write the given data in the cache file
  554.     *
  555.     * @param string $data data to put in cache
  556.     * @return boolean true if ok
  557.     * @access private
  558.     */
  559.     function _write($data)
  560.     {
  561.         $fp = @fopen($this->_file, "wb");
  562.         if ($fp) {
  563.             if ($this->_fileLocking) @flock($fp, LOCK_EX);
  564.             if ($this->_readControl) {
  565.                 @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
  566.             }
  567.             $len = strlen($data);
  568.             @fwrite($fp, $data, $len);
  569.             if ($this->_fileLocking) @flock($fp, LOCK_UN);
  570.             @fclose($fp);
  571.             return true;
  572.         }
  573.         $this->raiseError('Cache_Lite : Unable to write cache !', -1);
  574.         return false;
  575.     }
  576.     
  577.     /**
  578.     * Write the given data in the cache file and control it just after to avoir corrupted cache entries
  579.     *
  580.     * @param string $data data to put in cache
  581.     * @return boolean true if the test is ok
  582.     * @access private
  583.     */
  584.     function _writeAndControl($data)
  585.     {
  586.         $this->_write($data);
  587.         $dataRead = $this->_read($data);
  588.         return ($dataRead==$data);
  589.     }
  590.     
  591.     /**
  592.     * Make a control key with the string containing datas
  593.     *
  594.     * @param string $data data
  595.     * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
  596.     * @return string control key
  597.     * @access private
  598.     */
  599.     function _hash($data, $controlType)
  600.     {
  601.         switch ($controlType) {
  602.         case 'md5':
  603.             return md5($data);
  604.         case 'crc32':
  605.             return sprintf('% 32d', crc32($data));
  606.         case 'strlen':
  607.             return sprintf('% 32d', strlen($data));
  608.         default:
  609.             $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5);
  610.         }
  611.     }
  612.     
  613.  
  614. ?>
  615.